package com.edu.admin.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.edu.admin.entity.SysAdmin;
import com.edu.admin.service.RoleMenuService;

import java.util.*;

/**
 * 
 * @author Wangjiong
 * 
 */
@Component
public class TokenUtils {
	
	@Autowired
	RoleMenuService roleMenuService;
	
    public static final String ROLE_REFRESH_TOKEN = "ROLE_REFRESH_TOKEN";

    private static final String CLAIM_KEY_USER_ID = "ADMINID";
    private static final String CLAIM_KEY_ROLE_ID = "ROLEID";
    private static final String CLAIM_KEY_AUTHORITIES = "URLS";


    @Value("${token.secret}")
    private String secret;

    @Value("${token.expiration}")
    private Long access_token_expiration;

    @Value("${token.expiration}")
    private Long refresh_token_expiration;

    private final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;

    public SysAdmin getUserFromToken(String token) {
    	SysAdmin admin;
        try {
            final Claims claims = getClaimsFromToken(token);
            Long adminId = getAdminIdFromToken(token);
            String username = claims.getSubject();
            String url = claims.get(CLAIM_KEY_AUTHORITIES).toString();
            Long roleId = getRoleIdFromToken(token);
            admin = new SysAdmin();
            admin.setAdminId(adminId);
            admin.setAccount(username);
            admin.setRoleId(roleId);

            List<GrantedAuthority> authorities = new ArrayList<>();
            
            String[] urls = url.split(",");
     		for (int i= 0 ; i<urls.length ; i++)
     		{
     			if (!urls[i].isEmpty())
     			{
     				authorities.add(new SimpleGrantedAuthority(urls[i]));
     			}
     		}
     		admin.setAuthorities(authorities);
     		
        } catch (Exception e) {
        	admin = null;
        }
        return admin;
    }

    public Long getAdminIdFromToken(String token) {
        Long adminId;
        try {
            final Claims claims = getClaimsFromToken(token);
            adminId = Long.parseLong(String.valueOf(claims.get(CLAIM_KEY_USER_ID)));
        } catch (Exception e) {
        	adminId = null;
        }
        return adminId;
    }
    
    public Long getRoleIdFromToken(String token) {
        Long roleId;
        try {
            final Claims claims = getClaimsFromToken(token);
            roleId = Long.parseLong(String.valueOf(claims.get(CLAIM_KEY_ROLE_ID)));
        } catch (Exception e) {
        	roleId = null;
        }
        return roleId;
    }

    public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = claims.getIssuedAt();
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    public String generateAccessToken(SysAdmin admin) {
        Map<String, Object> claims = generateClaims(admin);
        claims.put(CLAIM_KEY_AUTHORITIES, admin.getRoleUrls());
        return generateAccessToken(admin.getAccount(), claims);
    }

    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
                && (!isTokenExpired(token));
    }

    public String refreshToken(String token) {
        String refreshedToken;
        try {
            final Claims claims = getClaimsFromToken(token);
            refreshedToken = generateAccessToken(claims.getSubject(), claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }


    public Boolean validateToken(String token, UserDetails userDetails) {
    	SysAdmin admin = (SysAdmin) userDetails;
        Long adminId = getAdminIdFromToken(token);
        String username = getUsernameFromToken(token);
        return (adminId == admin.getAdminId()
                && username.equals(admin.getAccount())
                && !isTokenExpired(token)

        );
    }

    public String generateRefreshToken(SysAdmin admin) {
        Map<String, Object> claims = generateClaims(admin);
        String roles[] = new String[]{TokenUtils.ROLE_REFRESH_TOKEN};
        claims.put(CLAIM_KEY_AUTHORITIES, JsonUtils.parseObjToJson(roles));
        return generateRefreshToken(admin.getRoleId().toString(), claims);
    }

    public void putToken(String userName, String token) {
    	JedisUtil.set(userName, token,  60 * 60 * 24);
    }

    public void deleteToken(String userName) {
    	JedisUtil.del(userName);
    }

    public boolean containToken(String userName, String token) {
        String redisToken =  JedisUtil.get(userName);
    	if (userName != null && redisToken!=null && redisToken.equals(token)) {
            return true;
        }
        return false;
    }
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    private Date generateExpirationDate(long expiration) {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }

    private Map<String, Object> generateClaims(SysAdmin admin) {
        Map<String, Object> claims = new HashMap<>(16);
        claims.put(CLAIM_KEY_USER_ID, admin.getAdminId());
        claims.put(CLAIM_KEY_ROLE_ID, admin.getRoleId());
        return claims;
    }

    private String generateAccessToken(String subject, Map<String, Object> claims) {
        return generateToken(subject, claims, access_token_expiration);
    }

    private String generateRefreshToken(String subject, Map<String, Object> claims) {
        return generateToken(subject, claims, refresh_token_expiration);
    }



    private String generateToken(String subject, Map<String, Object> claims, long expiration) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date())
                .setExpiration(generateExpirationDate(expiration))
                .compressWith(CompressionCodecs.DEFLATE)
                .signWith(SIGNATURE_ALGORITHM, secret)
                .compact();
    }

}
